package com.lmh.system.service.exam.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.lmh.common.core.constants.Constants;
import com.lmh.common.core.enums.ResultCode;
import com.lmh.common.security.exception.ServiceException;
import com.lmh.system.domain.exam.Exam;
import com.lmh.system.domain.exam.ExamQuestion;
import com.lmh.system.domain.exam.dto.ExamAddDTO;
import com.lmh.system.domain.exam.dto.ExamEditDTO;
import com.lmh.system.domain.exam.dto.ExamQueryDTO;
import com.lmh.system.domain.exam.dto.ExamQuestionDTO;
import com.lmh.system.domain.exam.vo.ExamDetailVO;
import com.lmh.system.domain.exam.vo.ExamVO;
import com.lmh.system.domain.question.Question;
import com.lmh.system.domain.question.vo.QuestionVO;
import com.lmh.system.manager.ExamCacheManager;
import com.lmh.system.mapper.exam.ExamMapper;
import com.lmh.system.mapper.exam.ExamQuestionMapper;
import com.lmh.system.mapper.question.QuestionMapper;
import com.lmh.system.service.exam.IExamService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Service
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper, ExamQuestion> implements IExamService {
    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private ExamQuestionMapper examQuestionMapper;

    @Autowired
    private ExamCacheManager examCacheManager;

    @Override
    public List<ExamVO> list(ExamQueryDTO examQueryDTO) {
        PageHelper.startPage(examQueryDTO.getPageNum(), examQueryDTO.getPageSize());
        List<ExamVO> examVOList = examMapper.selectExamList(examQueryDTO);
        return examVOList;
    }

    @Override
    public String add(ExamAddDTO examAddDTO) {
        //判断竞赛是否已经存在，以及判断开始时间和结束时间的合法性
        checkExamSaveParams(examAddDTO,null);
        Exam exam = new Exam();
        BeanUtil.copyProperties(examAddDTO,exam);

        examMapper.insert(exam);
        return exam.getExamId().toString();
    }

    @Override
    public boolean questionAdd(ExamQuestionDTO examQuestionDTO) {
        //先根据examId来确定竞赛是否存在
        Exam exam = getExam(examQuestionDTO.getExamId());

        // 当前端没有传递questionIdSet的时候，就表示只创建竞赛的基本信息而不选择创建竞赛的题目
        Set<Long> questionIdList = examQuestionDTO.getQuestionIdSet();
        if (CollectionUtil.isEmpty(questionIdList)) {
            return true;
        }

        // 检查是否有题目不存在
        List<Question> questionList = questionMapper.selectBatchIds(questionIdList);
        if (CollectionUtil.isEmpty(questionList) || questionList.size() < questionIdList.size()) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }

        // 当竞赛已经题目都没问题之后，就可以把该竞赛的题目存储进数据库中了
        return saveExamQuestion(examQuestionDTO, questionIdList);
    }

    @Override
    public ExamDetailVO detail(Long examId) {
        ExamDetailVO examDetailVO = new ExamDetailVO();
        // 获取竞赛基本信息
        Exam exam = getExam(examId);
        BeanUtil.copyProperties(exam,examDetailVO);
        // 获取竞赛的题目信息
        List<QuestionVO> questionVOList = examQuestionMapper.selectExamQuestionList(examId);
        if (CollectionUtil.isEmpty(questionVOList)) {
            return examDetailVO;
        }
        examDetailVO.setExamQuestionList(questionVOList);
        return examDetailVO;
    }

    @Override
    public int edit(ExamEditDTO examEditDTO) {
        // 先看前端传来的examId是否是正确的
        Exam exam = getExam(examEditDTO.getExamId());
        // 对修改之后的竞赛基本信息进行正确性校验
        checkExamSaveParams(examEditDTO, examEditDTO.getExamId());
        exam.setTitle(examEditDTO.getTitle());
        exam.setStartTime(examEditDTO.getStartTime());
        exam.setEndTime(examEditDTO.getEndTime());
        return examMapper.updateById(exam);
    }

    @Override
    public int questionDelete(Long examId, Long questionId) {
        Exam exam = getExam(examId);
        // 如果竞赛已经开始，那么就无法进行删除竞赛题目的操作
        checkExam(exam);
        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId)
                .eq(ExamQuestion::getQuestionId,questionId));
    }

    @Override
    public int examDelete(Long examId) {
        // 首先校验前端传来的参数是否正确，也就是看看该 examId 是否能在数据库中查找到竞赛
        Exam exam = getExam(examId);
        // 如果要删除竞赛，首先需要保证竞赛是处于未开赛的状态
        checkExam(exam);
        // 当确认可以删除之后，不仅要删除竞赛的基本信息，还需要删除该竞赛中的题目信息，也就是tb_exam_question中的数据
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId));
        return examMapper.deleteById(examId);

    }

    @Override
    public int publish(Long examId) {
        Exam exam = getExam(examId);
        checkExam(exam);
        // 检查该竞赛是否有题目
        Long questionCount = examQuestionMapper
                .selectCount(new LambdaQueryWrapper<ExamQuestion>()
                        .eq(ExamQuestion::getExamId,examId));
        if (questionCount <= 0) {
            throw new ServiceException(ResultCode.EXAM_NOT_HAS_QUESTION);
        }
        exam.setStatus(Constants.TRUE);
        examCacheManager.addCache(exam);
        return examMapper.updateById(exam);
    }

    @Override
    public int cancelPublish(Long examId) {
        Exam exam = getExam(examId);
        checkExam(exam);
        // 竞赛结束之后也无法撤销
        if (exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }
        exam.setStatus(Constants.FALSE);
        examCacheManager.deleteCache(examId);
        return examMapper.updateById(exam);
    }


    private static void checkExam(Exam exam) {
        if (exam.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_STARTED);
        }
    }

    private boolean saveExamQuestion(ExamQuestionDTO examQuestionDTO, Set<Long> questionIdList) {
        long num = examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>().eq(ExamQuestion::getExamId,examQuestionDTO.getExamId()));
        List<ExamQuestion> examQuestionList = new ArrayList<>();
        for (Long questionId : questionIdList) {
            ExamQuestion examQuestion = new ExamQuestion();
            examQuestion.setExamId(examQuestionDTO.getExamId());
            examQuestion.setQuestionId(questionId);
            examQuestion.setQuestionOrder((int)(num++));
            examQuestionList.add(examQuestion);
        }
        // 该方法是否执行成功就看saveBatch方法的返回结果是否为true
        return saveBatch(examQuestionList);
    }


    private Exam getExam(Long examId) {
        Exam exam = examMapper.selectById(examId);
        if (exam == null) {
            throw new ServiceException(ResultCode.EXAM_NOT_EXISTS);
        }
        return exam;
    }



    private void checkExamSaveParams(ExamAddDTO examSaveDTO, Long examId) {
        //1、竞赛标题是否重复进行判断   2、竞赛开始、结束时间进行判断
        List<Exam> examList = examMapper
                .selectList(new LambdaQueryWrapper<Exam>()
                        .eq(Exam::getTitle, examSaveDTO.getTitle())
                        .ne(examId != null, Exam::getExamId, examId));
        if (CollectionUtil.isNotEmpty(examList)) {
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
        if (examSaveDTO.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);  //竞赛开始时间不能早于当前时间
        }
        if (examSaveDTO.getStartTime().isAfter(examSaveDTO.getEndTime())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);
        }
    }
}
